package main

import (
	"fmt"
	"sort"
)

/*
*

1 切片(slice)是golang中一种特有的数据类型
2 数组有特定的用处，但是却有一些呆板(数组长度固定不可变)，所以在 Go 语言的代码里并不是特别常见。

	相对的切片却是随处可见的，切片是一种建立在数组类型之上的抽象，它构建在数组之上并且提供更强大的能力和便捷。

3 切片(sice)是对数组一个连续片段的引用，所以切片是一个引用类型。这个片段可以是整个数组，或者是由起始和终止索引标识的一些项的子集。

	需要注意的是，终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。

切片有3个字段的数据结构:一个是指向底层数组的指针，一个是切片的长度，一个是切片的容量。

*
*/
func main() {
	//test2()
	//test5()
	//test6()
	test7()

	var a = [...]int{3, 7, 8, 9, 1}
	// 对a进行降序排序
	sort.Sort(sort.Reverse(sort.IntSlice(a[:])))
	fmt.Println(a)
	sort.Ints(a[:])
	fmt.Println(a)
}

func test7() {

	var a = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
	slice := a[1:3]
	fmt.Println(slice)
	fmt.Printf("slice的地址：%p \n", &slice)
	fmt.Println("slice的长度：", len(slice))
	fmt.Println("slic的容量cap:", cap(slice))
}
func test6() {

	var a = make([]string, 5, 10)
	a[2] = "hello"
	fmt.Println("cap:", cap(a))
	fmt.Println("len:", len(a))
	fmt.Println(a)
	for i := 0; i < 12; i++ {
		a = append(a, fmt.Sprintf("%v", i))
	}
	fmt.Println("cap:", cap(a))
	fmt.Println("len:", len(a))
	fmt.Println(a)
}

func test5() {
	arr := [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}  // 声明一个数组并赋值 值类型
	slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} // 声明一个切片并赋值

	arr2 := arr
	slice2 := slice // 共享底层数组

	arr[2] = 111
	slice[2] = 222

	// 数组拷贝前后的两个变量的修改互不影响
	arr2[0] = 333
	// 切片拷贝前后两个变量共享底层数组，对一个切片的修改会影响另一个切片
	slice2[0] = 444

	fmt.Printf("%T  = %v \n", arr, arr)
	fmt.Printf("%T  = %v \n", arr2, arr2)
	fmt.Printf("%T  = %v \n", slice, slice)
	fmt.Printf("%T  = %v \n", slice2, slice2)

}

func test4() {
	slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	slice2 := make([]int, 20)
	fmt.Println(slice)  // [1 2 3 4 5 6 7 8 9]
	fmt.Println(slice2) // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

	//拷贝
	copy(slice2, slice)
	fmt.Println(slice2) //[1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0 0]
}

// 切片的追加
func test3() {
	// 定义数组
	intarray := [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}

	// 切片构建在数组之上:
	slice := intarray[1:3]
	fmt.Println(slice)
	fmt.Println("长度：", len(slice))

	// 追加 返回一个新的切片
	slice2 := append(slice, 10)
	slice3 := append(slice2, 10)
	fmt.Println(slice2)
	fmt.Printf("%p \n", &slice)
	fmt.Printf("%p \n", &slice2)
	fmt.Printf("%p \n", &slice3)
	// 底层原理:
	//1.底层追加元素的时候对数组进行扩容，老数组扩容为新数组:
	//2.创建一个新数组，将老数组中的4,7,3复制到新数组中，在新数组中追加88,5
	//3.slice2 底层数组的指向 指向的是新数组
	//5.底层的新数组 不能直接维护，需要通过切片间接维护操作。
	slice = append(slice, slice2...)
	fmt.Println(slice)
}

// 通过make 初始化切片
func test2() {
	fmt.Println("test2----------------------------------------")
	// 定义切片:make函数的三个参数:1.切片类型2.切片长度3.切片的容量
	slice := make([]int, 5, 10)
	fmt.Println(slice)
	fmt.Println("长度：", len(slice))
	fmt.Println("容量：", cap(slice))

	slice[1] = 123
	fmt.Println(slice)

	// 定义切片并赋值
	// 定一个切片，直接就指定具体数组，使用原理类似make的方式。
	slice1 := []int{1, 2, 3, 4, 5}
	fmt.Println(slice1)
	fmt.Println("长度：", len(slice1))
	fmt.Println("容量：", cap(slice1))
	fmt.Printf("类型：%t \n", slice1)
	fmt.Printf("类型：%T \n", slice1)
}

func test1() {
	// 定义数组
	var intarray [5]int = [5]int{1, 2, 3, 4, 5}

	//切片构建在数组之上:
	//定义一个切片名字为slice,[]动态变化的数组长度不写，int类型，intarr是原数组
	//[1:3]切片 -切出的一段片段 -索引:从1开始，到3结束(不包含3)-[1,3)
	// 定义切片
	var slice []int = intarray[1:3]
	fmt.Println(slice)
	slice1 := intarray[:3] // 从0开始，到3结束(不包含3)
	slice2 := intarray[1:] // 从1开始，到数组结束
	slice3 := intarray[:]  // 从0开始，到数组结束
	fmt.Println("切片的三种简写方式：")
	fmt.Println(slice1)
	fmt.Println(slice2)
	fmt.Println(slice3)

	// 切片再次切片
	slice4 := slice[:]
	fmt.Println("二次切片：", slice4)
	slice4[1] = 9898
	fmt.Println("二次切片：", slice4)
	fmt.Println("原切片：", slice)
	fmt.Println("数组：", intarray)

	//切片的长度和容量
	//len() -切片的长度
	//cap() -切片的容量
	fmt.Println("切片的长度和容量：", len(slice), cap(slice))
	fmt.Println("数组的长度和容量：", len(intarray), cap(intarray))

	fmt.Printf("数组中下标为1位置的地址： %p \n", &intarray[1])
	fmt.Printf("切片中下标为0位置的地址： %p \n", &slice[0])

	slice[1] = 1212 //修改切片中的值 同时也改了数组的值
	fmt.Println(slice)
	fmt.Println(intarray)
}
